/*
 * Copyright (c) 2011-2014, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.alg.misc;

import boofcv.alg.InputSanityCheck;
import boofcv.struct.image.*;

/**
 * Functions which perform basic arithmetic (e.g. addition, subtraction, multiplication, or division) on a pixel by pixel basis.
 *
 * <p>DO NOT MODIFY: Generated by {@link boofcv.alg.misc.GeneratePixelMath}.</p>
 *
 * @author Peter Abeles
 */
public class PixelMath {

	/**
	 * Sets each pixel in the output image to be the absolute value of the input image.
	 * Both the input and output image can be the same instance.
	 * 
	 * @param input The input image. Not modified.
	 * @param output Where the absolute value image is written to. Modified.
	 */
	public static void abs( ImageSInt8 input , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);
		
		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = (byte)Math.abs(input.data[indexSrc]);
			}
		}
	}

	/**
	 * Changes the sign of every pixel in the image: output[x,y] = -input[x,y]
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the inverted image is written to. Modified.
	 */
	public static void invert( ImageSInt8 input , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = (byte)-input.data[indexSrc];
			}
		}
	}

	/**
	 * Sets each pixel in the output image to be the absolute value of the input image.
	 * Both the input and output image can be the same instance.
	 * 
	 * @param input The input image. Not modified.
	 * @param output Where the absolute value image is written to. Modified.
	 */
	public static void abs( ImageSInt16 input , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);
		
		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = (short)Math.abs(input.data[indexSrc]);
			}
		}
	}

	/**
	 * Changes the sign of every pixel in the image: output[x,y] = -input[x,y]
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the inverted image is written to. Modified.
	 */
	public static void invert( ImageSInt16 input , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = (short)-input.data[indexSrc];
			}
		}
	}

	/**
	 * Sets each pixel in the output image to be the absolute value of the input image.
	 * Both the input and output image can be the same instance.
	 * 
	 * @param input The input image. Not modified.
	 * @param output Where the absolute value image is written to. Modified.
	 */
	public static void abs( ImageSInt32 input , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);
		
		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = Math.abs(input.data[indexSrc]);
			}
		}
	}

	/**
	 * Changes the sign of every pixel in the image: output[x,y] = -input[x,y]
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the inverted image is written to. Modified.
	 */
	public static void invert( ImageSInt32 input , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = -input.data[indexSrc];
			}
		}
	}

	/**
	 * Sets each pixel in the output image to be the absolute value of the input image.
	 * Both the input and output image can be the same instance.
	 * 
	 * @param input The input image. Not modified.
	 * @param output Where the absolute value image is written to. Modified.
	 */
	public static void abs( ImageSInt64 input , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);
		
		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = Math.abs(input.data[indexSrc]);
			}
		}
	}

	/**
	 * Changes the sign of every pixel in the image: output[x,y] = -input[x,y]
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the inverted image is written to. Modified.
	 */
	public static void invert( ImageSInt64 input , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = -input.data[indexSrc];
			}
		}
	}

	/**
	 * Sets each pixel in the output image to be the absolute value of the input image.
	 * Both the input and output image can be the same instance.
	 * 
	 * @param input The input image. Not modified.
	 * @param output Where the absolute value image is written to. Modified.
	 */
	public static void abs( ImageFloat32 input , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);
		
		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = Math.abs(input.data[indexSrc]);
			}
		}
	}

	/**
	 * Changes the sign of every pixel in the image: output[x,y] = -input[x,y]
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the inverted image is written to. Modified.
	 */
	public static void invert( ImageFloat32 input , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = -input.data[indexSrc];
			}
		}
	}

	/**
	 * Sets each pixel in the output image to be the absolute value of the input image.
	 * Both the input and output image can be the same instance.
	 * 
	 * @param input The input image. Not modified.
	 * @param output Where the absolute value image is written to. Modified.
	 */
	public static void abs( ImageFloat64 input , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);
		
		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = Math.abs(input.data[indexSrc]);
			}
		}
	}

	/**
	 * Changes the sign of every pixel in the image: output[x,y] = -input[x,y]
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the inverted image is written to. Modified.
	 */
	public static void invert( ImageFloat64 input , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = -input.data[indexSrc];
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageUInt8 input , double value , ImageUInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (byte)((input.data[indexSrc] & 0xFF) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageUInt8 input , double value , int lower , int upper , ImageUInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] & 0xFF) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (byte)val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageUInt8 input , double denominator , ImageUInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (byte)((input.data[indexSrc] & 0xFF) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageUInt8 input , double denominator , int lower , int upper , ImageUInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] & 0xFF) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (byte)val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageUInt8 input , int value , ImageUInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (byte)((input.data[indexSrc] & 0xFF) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageUInt8 input , int value , int lower , int upper , ImageUInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (input.data[indexSrc] & 0xFF) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (byte)val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageUInt8 img , int min , int max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		byte[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				int value = data[index]& 0xFF;
				if( value < min )
					data[index] = (byte)min;
				else if( value > max )
					data[index] = (byte)max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageUInt8 imgA , ImageUInt8 imgB , ImageUInt8 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = (byte)Math.abs((imgA.data[indexA] & 0xFF) - (imgB.data[indexB] & 0xFF));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageUInt8> input , ImageUInt8 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageUInt8[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				int total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ]& 0xFF;
				}
				output.data[indexOutput] = (byte)(total / bands.length);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt8 input , double value , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (byte)((input.data[indexSrc] ) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt8 input , double value , int lower , int upper , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] ) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (byte)val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt8 input , double denominator , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (byte)((input.data[indexSrc] ) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt8 input , double denominator , int lower , int upper , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] ) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (byte)val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt8 input , int value , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (byte)((input.data[indexSrc] ) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt8 input , int value , int lower , int upper , ImageSInt8 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (input.data[indexSrc] ) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (byte)val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageSInt8 img , int min , int max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		byte[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				int value = data[index];
				if( value < min )
					data[index] = (byte)min;
				else if( value > max )
					data[index] = (byte)max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageSInt8 imgA , ImageSInt8 imgB , ImageSInt8 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = (byte)Math.abs((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageSInt8> input , ImageSInt8 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageSInt8[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				int total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ];
				}
				output.data[indexOutput] = (byte)(total / bands.length);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageUInt16 input , double value , ImageUInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (short)((input.data[indexSrc] & 0xFFFF) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageUInt16 input , double value , int lower , int upper , ImageUInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] & 0xFFFF) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (short)val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageUInt16 input , double denominator , ImageUInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (short)((input.data[indexSrc] & 0xFFFF) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageUInt16 input , double denominator , int lower , int upper , ImageUInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] & 0xFFFF) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (short)val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageUInt16 input , int value , ImageUInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (short)((input.data[indexSrc] & 0xFFFF) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageUInt16 input , int value , int lower , int upper , ImageUInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (input.data[indexSrc] & 0xFFFF) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (short)val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageUInt16 img , int min , int max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		short[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				int value = data[index]& 0xFFFF;
				if( value < min )
					data[index] = (short)min;
				else if( value > max )
					data[index] = (short)max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageUInt16 imgA , ImageUInt16 imgB , ImageUInt16 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = (short)Math.abs((imgA.data[indexA] & 0xFFFF) - (imgB.data[indexB] & 0xFFFF));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageUInt16> input , ImageUInt16 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageUInt16[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				int total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ]& 0xFFFF;
				}
				output.data[indexOutput] = (short)(total / bands.length);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt16 input , double value , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (short)((input.data[indexSrc] ) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt16 input , double value , int lower , int upper , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] ) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (short)val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt16 input , double denominator , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (short)((input.data[indexSrc] ) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt16 input , double denominator , int lower , int upper , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] ) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (short)val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt16 input , int value , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (short)((input.data[indexSrc] ) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt16 input , int value , int lower , int upper , ImageSInt16 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (input.data[indexSrc] ) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = (short)val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageSInt16 img , int min , int max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		short[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				int value = data[index];
				if( value < min )
					data[index] = (short)min;
				else if( value > max )
					data[index] = (short)max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageSInt16 imgA , ImageSInt16 imgB , ImageSInt16 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = (short)Math.abs((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageSInt16> input , ImageSInt16 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageSInt16[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				int total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ];
				}
				output.data[indexOutput] = (short)(total / bands.length);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt32 input , double value , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (int)((input.data[indexSrc] ) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt32 input , double value , int lower , int upper , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] ) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt32 input , double denominator , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (int)((input.data[indexSrc] ) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt32 input , double denominator , int lower , int upper , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (int)((input.data[indexSrc] ) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt32 input , int value , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt32 input , int value , int lower , int upper , ImageSInt32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				int val = (input.data[indexSrc] ) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageSInt32 img , int min , int max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		int[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				int value = data[index];
				if( value < min )
					data[index] = min;
				else if( value > max )
					data[index] = max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageSInt32 imgA , ImageSInt32 imgB , ImageSInt32 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = (int)Math.abs((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageSInt32> input , ImageSInt32 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageSInt32[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				int total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ];
				}
				output.data[indexOutput] = (total / bands.length);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt64 input , double value , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (long)((input.data[indexSrc] ) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageSInt64 input , double value , long lower , long upper , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				long val = (long)((input.data[indexSrc] ) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt64 input , double denominator , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = (long)((input.data[indexSrc] ) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageSInt64 input , double denominator , long lower , long upper , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				long val = (long)((input.data[indexSrc] ) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt64 input , long value , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageSInt64 input , long value , long lower , long upper , ImageSInt64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				long val = (input.data[indexSrc] ) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageSInt64 img , long min , long max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		long[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				long value = data[index];
				if( value < min )
					data[index] = min;
				else if( value > max )
					data[index] = max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageSInt64 imgA , ImageSInt64 imgB , ImageSInt64 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = (long)Math.abs((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageSInt64> input , ImageSInt64 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageSInt64[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				long total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ];
				}
				output.data[indexOutput] = (total / bands.length);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageFloat32 input , float value , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageFloat32 input , float value , float lower , float upper , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				float val = ((input.data[indexSrc] ) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageFloat32 input , float denominator , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageFloat32 input , float denominator , float lower , float upper , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				float val = ((input.data[indexSrc] ) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageFloat32 input , float value , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageFloat32 input , float value , float lower , float upper , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				float val = (input.data[indexSrc] ) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageFloat32 img , float min , float max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		float[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				float value = data[index];
				if( value < min )
					data[index] = min;
				else if( value > max )
					data[index] = max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageFloat32 imgA , ImageFloat32 imgB , ImageFloat32 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = Math.abs((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageFloat32> input , ImageFloat32 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageFloat32[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				float total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ];
				}
				output.data[indexOutput] = (total / bands.length);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageFloat64 input , double value , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) * value);
			}
		}
	}

	/**
	 * Multiply each element by a scalar value and bounds the result. Both input and output images can
	 * be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What each element is multiplied by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void multiply( ImageFloat64 input , double value , double lower , double upper , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				double val = ((input.data[indexSrc] ) * value);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Divide each element by a scalar value. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageFloat64 input , double denominator , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) / denominator);
			}
		}
	}

	/**
	 * Divide each element by a scalar value and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param denominator What each element is divided by.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void divide( ImageFloat64 input , double denominator , double lower , double upper , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				double val = ((input.data[indexSrc] ) / denominator);
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Add a scalar value to each element. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageFloat64 input , double value , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				output.data[indexDst] = ((input.data[indexSrc] ) + value);
			}
		}
	}

	/**
	 * Add a scalar value to each element and bounds the result. Both input and output images can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param value What is added to each element.
	 * @param lower Lower bound on output
	 * @param upper Upper bound on output
	 * @param output The output image. Modified.
	 */
	public static void plus( ImageFloat64 input , double value , double lower , double upper , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++, indexDst++ ) {
				double val = (input.data[indexSrc] ) + value;
				if( val < lower ) val = lower;
				if( val > upper ) val = upper;
				output.data[indexDst] = val;
			}
		}
	}

	/**
	 * Bounds image pixels to be between these two values
	 * 
	 * @param img Image
	 * @param min minimum value.
	 * @param max maximum value.
	 */
	public static void boundImage( ImageFloat64 img , double min , double max ) {
		final int h = img.getHeight();
		final int w = img.getWidth();

		double[] data = img.data;

		for (int y = 0; y < h; y++) {
			int index = img.getStartIndex() + y * img.getStride();
			int indexEnd = index+w;
			// for(int x = 0; x < w; x++ ) {
			for (; index < indexEnd; index++) {
				double value = data[index];
				if( value < min )
					data[index] = min;
				else if( value > max )
					data[index] = max;
			}
		}
	}

	/**
	 * <p>
	 * Computes the absolute value of the difference between each pixel in the two images.<br>
	 * d(x,y) = |img1(x,y) - img2(x,y)|
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param diff Absolute value of difference image. Modified.
	 */
	public static void diffAbs( ImageFloat64 imgA , ImageFloat64 imgB , ImageFloat64 diff ) {
		InputSanityCheck.checkSameShape(imgA,imgB,diff);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexDiff = diff.getStartIndex() + y * diff.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexDiff++ ) {
				diff.data[indexDiff] = Math.abs((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Computes the average for each pixel across all bands in the {@link MultiSpectral} image.
	 * 
	 * @param input MultiSpectral image
	 * @param output Gray scale image containing average pixel values
	 */
	public static void averageBand( MultiSpectral<ImageFloat64> input , ImageFloat64 output ) {
		final int h = input.getHeight();
		final int w = input.getWidth();

		ImageFloat64[] bands = input.bands;
		
		for (int y = 0; y < h; y++) {
			int indexInput = input.getStartIndex() + y * input.getStride();
			int indexOutput = output.getStartIndex() + y * output.getStride();

			int indexEnd = indexInput+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexInput < indexEnd; indexInput++, indexOutput++ ) {
				double total = 0;
				for( int i = 0; i < bands.length; i++ ) {
					total += bands[i].data[ indexInput ];
				}
				output.data[indexOutput] = (total / bands.length);
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageUInt8 imgA , ImageUInt8 imgB , ImageUInt16 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (short)((imgA.data[indexA] & 0xFF) + (imgB.data[indexB] & 0xFF));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageUInt8 imgA , ImageUInt8 imgB , ImageInt16 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (short)((imgA.data[indexA] & 0xFF) - (imgB.data[indexB] & 0xFF));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageSInt8 imgA , ImageSInt8 imgB , ImageSInt16 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (short)((imgA.data[indexA] ) + (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageSInt8 imgA , ImageSInt8 imgB , ImageSInt16 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (short)((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageUInt16 imgA , ImageUInt16 imgB , ImageSInt32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (int)((imgA.data[indexA] & 0xFFFF) + (imgB.data[indexB] & 0xFFFF));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageUInt16 imgA , ImageUInt16 imgB , ImageSInt32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (int)((imgA.data[indexA] & 0xFFFF) - (imgB.data[indexB] & 0xFFFF));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageSInt16 imgA , ImageSInt16 imgB , ImageSInt32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (int)((imgA.data[indexA] ) + (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageSInt16 imgA , ImageSInt16 imgB , ImageSInt32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (int)((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageSInt32 imgA , ImageSInt32 imgB , ImageSInt32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (int)((imgA.data[indexA] ) + (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageSInt32 imgA , ImageSInt32 imgB , ImageSInt32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (int)((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageSInt64 imgA , ImageSInt64 imgB , ImageSInt64 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (long)((imgA.data[indexA] ) + (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageSInt64 imgA , ImageSInt64 imgB , ImageSInt64 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = (long)((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageFloat32 imgA , ImageFloat32 imgB , ImageFloat32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) + (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageFloat32 imgA , ImageFloat32 imgB , ImageFloat32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise multiplication<br>
	 * output(x,y) = imgA(x,y) * imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void multiply( ImageFloat32 imgA , ImageFloat32 imgB , ImageFloat32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) * (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise division<br>
	 * output(x,y) = imgA(x,y) / imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void divide( ImageFloat32 imgA , ImageFloat32 imgB , ImageFloat32 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) / (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Sets each pixel in the output image to log( 1 + input(x,y)) of the input image.
	 * Both the input and output image can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the log image is written to. Modified.
	 */
	public static void log( ImageFloat32 input , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = (float)Math.log(1 + input.data[indexSrc]);
			}
		}
	}

	/**
	 * Raises each pixel in the input image to the power of two. Both the input and output image can be the 
	 * same instance.	 *
	 * @param input The input image. Not modified.
	 * @param output Where the pow2 image is written to. Modified.
	 */
	public static void pow2( ImageFloat32 input , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				float v = input.data[indexSrc];
				output.data[indexDst] = v*v;
			}
		}
	}

	/**
	 * Computes the square root of each pixel in the input image. Both the input and output image can be the
	 * same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the sqrt() image is written to. Modified.
	 */
	public static void sqrt( ImageFloat32 input , ImageFloat32 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = (float)Math.sqrt(input.data[indexSrc]);
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise addition<br>
	 * output(x,y) = imgA(x,y) + imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void add( ImageFloat64 imgA , ImageFloat64 imgB , ImageFloat64 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) + (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise subtraction.<br>
	 * output(x,y) = imgA(x,y) - imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void subtract( ImageFloat64 imgA , ImageFloat64 imgB , ImageFloat64 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) - (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise multiplication<br>
	 * output(x,y) = imgA(x,y) * imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void multiply( ImageFloat64 imgA , ImageFloat64 imgB , ImageFloat64 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) * (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * <p>
	 * Performs pixel-wise division<br>
	 * output(x,y) = imgA(x,y) / imgB(x,y)
	 * </p>
	 * @param imgA Input image. Not modified.
	 * @param imgB Input image. Not modified.
	 * @param output Output image. Modified.
	 */
	public static void divide( ImageFloat64 imgA , ImageFloat64 imgB , ImageFloat64 output ) {
		InputSanityCheck.checkSameShape(imgA,imgB,output);
		
		final int h = imgA.getHeight();
		final int w = imgA.getWidth();

		for (int y = 0; y < h; y++) {
			int indexA = imgA.getStartIndex() + y * imgA.getStride();
			int indexB = imgB.getStartIndex() + y * imgB.getStride();
			int indexOut = output.getStartIndex() + y * output.getStride();
			
			int indexEnd = indexA+w;
			// for(int x = 0; x < w; x++ ) {
			for (; indexA < indexEnd; indexA++, indexB++, indexOut++ ) {
				output.data[indexOut] = ((imgA.data[indexA] ) / (imgB.data[indexB] ));
			}
		}
	}

	/**
	 * Sets each pixel in the output image to log( 1 + input(x,y)) of the input image.
	 * Both the input and output image can be the same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the log image is written to. Modified.
	 */
	public static void log( ImageFloat64 input , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = Math.log(1 + input.data[indexSrc]);
			}
		}
	}

	/**
	 * Raises each pixel in the input image to the power of two. Both the input and output image can be the 
	 * same instance.	 *
	 * @param input The input image. Not modified.
	 * @param output Where the pow2 image is written to. Modified.
	 */
	public static void pow2( ImageFloat64 input , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				double v = input.data[indexSrc];
				output.data[indexDst] = v*v;
			}
		}
	}

	/**
	 * Computes the square root of each pixel in the input image. Both the input and output image can be the
	 * same instance.
	 *
	 * @param input The input image. Not modified.
	 * @param output Where the sqrt() image is written to. Modified.
	 */
	public static void sqrt( ImageFloat64 input , ImageFloat64 output ) {

		InputSanityCheck.checkSameShape(input,output);

		for( int y = 0; y < input.height; y++ ) {
			int indexSrc = input.startIndex + y* input.stride;
			int indexDst = output.startIndex + y* output.stride;
			int end = indexSrc + input.width;

			for( ; indexSrc < end; indexSrc++ , indexDst++) {
				output.data[indexDst] = Math.sqrt(input.data[indexSrc]);
			}
		}
	}

}
